基于区块链的图像分享 您所在的位置:网站首页 图像哈希距离 区块链 基于区块链的图像分享

基于区块链的图像分享

2023-06-18 07:08| 来源: 网络整理| 查看: 265

本文是对该系统实现的一个总结和备忘 本系统的结构图大致如下图,前端采用vue及element-ui plus,后端使用Django,数据库使用Mysql,区块链环境使用ganache-cli,文件系统使用go-ipfs,后端和区块链使用web3.py,后端与ipfs交互使用ipfshttpclient

环境搭建 区块链相关环境搭建 ganache-cli

下载参考: https://github.com/trufflesuite/ganache-cli 如图自动生成了10个各有100以太的账户 智能合约编写使用remix,是一个在线的编辑器https://remix.ethereum.org/ 简单的使用流程如下 编译成功后,在此处可以复制生成的abi 之后部署合约,首先需要连接本地的区块链环境,如下所示,选择web3 provider 注意应该对应自己本地环境对应的地址 连接后,在下图一处部署合约,部署成功后可在二处复制合约地址,在三处调用测试合约的相关功能。

web3.py使用

pip install web3 参考链接: web3.py文档 https://www.pianshen.com/article/18638804/ https://blog.csdn.net/qq_41907714/category_10590479.html https://www.jianshu.com/p/520516c7d377?utm_campaign https://blog.csdn.net/weixin_39430411/article/details/104270968 https://www.freesion.com/article/1744847080/ https://www.freesion.com/article/1744847080/ https://blog.csdn.net/BF02jgtRS00XKtCx/article/details/106030165 连接区块链环境大致如下:

#连接区块链 web3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545')) #得到合约部署的abi,我这里放入文件中,所以从文件中读取 with open('tool/test.abi') as file: myabi = json.loads(file.read()) #其中address为合约部署的地址 contract = web3.eth.contract(address=address, abi=myabi) #可以判断区块链连接是否成功 if web3.eth.getBlock(0) is None: print("连接失败") elif web3.isConnected(): print("连接成功") #调用合约的两种方式 contract.functions.调用的函数().call() contract.functions.调用的函数().transact({'from':web3.eth.accounts[0]}) ipfs

这里只使用命令行版,下载后通过ipfs daemon启动,如下所示 这里也是通过Python与其交互,所以安装 pip install ipfshttpclient 具体使用见:https://github.com/ipfs-shipyard/py-ipfs-http-client

报错解决 pip install web3报错

AttributeError: 'NoneType' object has no attribute 'bytes' 使用如下命令解决 easy_install -U pip 再次运行报错 error: Microsoft Visual C++ 14.0 is required. Get it with "Build Tools for Visual Studio" 在如下连接下载对应版本,其中的cp对应版本,然后pip install 对应模块 https://www.lfd.uci.edu/~gohlke/pythonlibs/ 下载了三个模块: cytoolz.dicttoolz lru-dict bitarray._bitarray 但是bitarray还是报错,使用下面连接中第三种方法解决 https://zhuanlan.zhihu.com/p/126669852

运行ganache-cli出错

报错信息如下

Ganache CLI v6.10.1 (ganache-core: 2.11.2) Error: Callback was already called. at C:\Users\AppData\Roaming\npm\node_modules\ganache-cli\build\ganache-core.node.cli.js:19:276 at s. (C:\Users\AppData\Roaming\npm\node_modules\ganache-cli\build\ganache-core.node.cli.js:19:2238) at s.emit (events.js:315:20) at s.destroy (C:\Users\AppData\Roaming\npm\node_modules\ganache-cli\build\ganache-core.node.cli.js:39:744230) at finish (internal/streams/writable.js:670:14) at processTicksAndRejections (internal/process/task_queues.js:80:21)

使用如下命令卸载后重装

npm uninstall -g ganache-cli npm install -g ganache-cli 使用ipfs时报错

报错如下:

https://dist.ipfs.io/ The migrations of fs-repo failed: failed to find latest fs-repo-migrations: http.DefaultClient.Do error: Get "https://ipfs.io/ipfs/QmYRLRDKobvg1AXTGeK5Xk6ntWTsjGiHbyNKhWfz7koGpa/fs-repo-migrations/versions": dial tcp 103.252.115.153:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. If you think this is a bug, please file an issue and include this whole log output. https://github.com/ipfs/fs-repo-migrations Error: failed to find latest fs-repo-migrations: http.DefaultClient.Do error: Get "https://ipfs.io/ipfs/QmYRLRDKobvg1AXTGeK5Xk6ntWTsjGiHbyNKhWfz7koGpa/fs-repo-migrations/versions": dial tcp 103.252.115.153:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.

可能因为之前用过ipfs,之后又删除了,产生了冲突,可以将C:\Users\自己的路径.ipfs该处的.ipfs文件删除

功能实现 合约编写

solidity中文文档 使用智能合约主要进行以下功能的设计:

任何系统和用户都可以调用本文设计的智能合约,将自己原创作品的版权信息登记在区块链中。 对于图像的下载,能够对智能合约将下载者和下载图像的相关信息进行记录,用作之后侵权判定的证据之一。 当侵权发生时,可以通过智能合约查询侵权图片的相关信息和图片的下载记录,并对侵权行为进行判定。 用于实现图像分享平台的补充功能:基于智能合约实现用户之间的转账,账户信息的查询等操作。 图像的版权信息组成 变量名 类型 含义 Name string 图片上传时的名字 PictureID uint 图片唯一的编号,由合约自动递增生成 PictureHash string 图片经过ORB算法计算得到的特征从而得到的Hash,在上传到IPFS上是,根据其内容生成,也是访问该图片存储在IPFS上的特征时所需要的唯一链接 OwnerID string 图片上传者唯一的编号,11位数字组成 UploadTime uint 时间戳,表示上传时间,为1970年后经过的秒数 HashID bytes 将以上变量数值使用keccak256算法得到的哈希值 实现代码 pragma solidity 0.8.0; contract ImageSharing{ uint public PictureID; //用于图像版权信息保存,通过图像编号映射到该图像 mapping(uint=>Picture)Stores; //用于保存下载记录,通过图像编号映射到下一层映射A,A保存下载记录,通过下载序号映射到下载记录DownUser mapping(uint=>mapping(uint=>DownUser)) User; //通过用户编号映射对应的余额,因为solidity不支持浮点数的保存和运算,又因为系统场景中,金钱的交易一般为小数点后有两位的小数,所以在保存前和查询后需要对金额进行乘以100和除以100的操作 mapping(string=>uint) Money; //保存下载记录 struct DownUser{ uint DownTime; string UserID; } //图片版权信息 struct Picture { uint PictureID; string name; string PictureHash; string OwnerID; uint UpLoadTime; bytes32 HashID; uint DownNumber; } event backPictureID(uint); constructor(){ PictureID=0; } //向账户存钱 function AddMoney(string memory _UserID,uint _num)public returns(bool ){ Money[_UserID]=Money[_UserID]+_num; return (true); } //从账户取钱 function SubMoney(string memory _UserID,uint _num)public returns(bool ){ Money[_UserID]=Money[_UserID]-_num; return (true); } //将钱从一个账户向另一个账户转移 function tran(string memory _UserID1,string memory _UserID2,uint _num)public returns(bool ){ Money[_UserID1]=Money[_UserID1]-_num; Money[_UserID2]=Money[_UserID2]+_num; return (true); } //查询账户余额 function GetMoney(string memory _UserID)public view returns(uint ){ return ( Money[_UserID] ); } //上传图片,保存图片信息,生成图像唯一编号PictureID,并对版权信息哈希后保存为HashID function Upload( string memory _name, string memory _PictureHash, string memory _OwnerID, uint _UpLoadTime )public{ PictureID+=1; bytes32 HashID=keccak256(abi.encodePacked(PictureID,_name,_PictureHash,_OwnerID,_UpLoadTime)); Picture memory picture=Picture(PictureID,_name,_PictureHash,_OwnerID,_UpLoadTime,HashID,0); Stores[PictureID] = picture; emit backPictureID(PictureID); } //得到图像相关信息,图像的编号PictureID,图像的名称name,图像特征的地址PictureHash,图像上传者编号OwnerID,图像上传时间,HashID function GetPicture(uint _PictureID)public view returns(uint,string memory,string memory,string memory,uint,bytes32 ){ Picture memory picture = Stores[_PictureID]; return ( picture.PictureID, picture.name, picture.PictureHash, picture.OwnerID, picture.UpLoadTime, picture.HashID ); } //保存下载记录 function Download(uint _PictureID,uint _DownTime,string memory _UserID)public{ Stores[_PictureID].DownNumber++; Picture memory picture = Stores[_PictureID]; DownUser memory downuser=DownUser(_DownTime,_UserID); User[_PictureID][picture.DownNumber]=downuser; } //用于侵权判定,判断该用户是否下载过该图片 function judge(uint _PictureID,string memory _UserID)public view returns(bool,uint){ Picture memory picture = Stores[_PictureID]; for(uint i=1;i 0): water.append(255) else: water.append(0) j = j + 1 i = i + 1 water = np.array(water) water_img = water.reshape((144, 144)) return water_img # cv2.imwrite(r'C:/Users/Desktop/huiwater2.png', water_img)

效果如图

可见水印

为了防止截屏图片,添加可见水印

from PIL import Image, ImageDraw # 添加可见水印 def new_logo(name): # 简单的嵌入操作,主要是粘贴 im1 = Image.open('watermark/watermark.png') # 嵌的水印 r, g, b, a = im1.split() im2 = Image.open(name) # 被嵌的大图片,name为嵌入图片路径 x, y = im2.size im2.paste(im1, (int(x / 2 - 150), int(y / 2 - 35)), mask=a) im2.save('shuiyin/'+name)

效果如图:

沙箱支付

系统有支付功能,这里使用手机支付宝扫码支付的方式进行充值,使用Python-alipay-sdk,使用说明:https://github.com/fzlee/alipay/blob/master/docs/apis.zh-hans.md#verification 沙箱环境的配置参考: https://pea328.blog.csdn.net/article/details/102502851?utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control https://www.cnblogs.com/qikeyishu/p/11564756.html 在电脑上使用沙箱环境支付操作时,出现“提示存在钓鱼风险”的页面,可能是在浏览器中同时登陆了真正的支付宝账号,所以在使用时从另一个浏览器进行调试。

#以下为程序中配置的信息 from alipay import AliPay alipay_public_key_string = """-----BEGIN PUBLIC KEY----- 自己配置的公钥 -----END PUBLIC KEY-----""" app_private_key_string = """-----BEGIN RSA PRIVATE KEY----- 自己配置的私钥 -----END RSA PRIVATE KEY-----""" alipay = AliPay( appid = '2021000117650', app_notify_url = ' ', app_private_key_string = app_private_key_string, alipay_public_key_string = alipay_public_key_string, debug=True, )

充值时前端生成uuid,请求charge函数处理,后端会返回一个支付链接,前端打开 uuid生成参考:https://www.cnblogs.com/lingfenglian/p/12605465.html 如下所示:

charge(){ this.chargeDialogVisible = true var that = this; that.axios .post(that.baseURL + "charge/",{ sessionID: sessionStorage.sessionID, amount:that.chargeNum, uuid:guid() }) .then((response) => { const status = response.data.status; if (status === 1) { that.$message({ message: response.data.msg, showClose: true, type: "success", }); // window.location.href = response.data.url window.open(response.data.url) } else { that.$message({ message: response.data.msg, showClose: true, type: "warning", }); } }) .catch(function (err) { that.$message({ message: err, showClose: true, type: "warning", }); }); },

后端charge函数生成支付信息,通过return_url进行支付成功之后的操作 return_url会请求v_charge,v_charge进行支付后的判定和信息的记录

def Charge(request): #得到信息 data=json.loads(request.body.decode('utf-8')) sessionID=data['sessionID'] session = Session.objects.get(pk=sessionID) UserID=session.get_decoded()['username'] uuid=data['uuid'] amount=data['amount'] # 发起支付请求 order_string = alipay.api_alipay_trade_page_pay( out_trade_no=uuid, total_amount=amount, subject='充值', return_url='http://192.168.43.10:8000/vcharge/?sessionID='+sessionID ) pay_url = 'https://openapi.alipaydev.com/gateway.do?' + order_string # obj_charge=charge(uuid=uuid,status=0,kind=1,ID=UserID,amount=a) obj_charge=charge(uuid=uuid,status=0,kind=1,userID=UserID,amount=amount) obj_charge.save() return JsonResponse({'status': 1, 'msg': '成功','url':pay_url}) def v_charge(request): orderID=request.GET.get('out_trade_no') print(orderID) sessionID=request.GET.get('sessionID') session = Session.objects.get(pk=sessionID) UserID=session.get_decoded()['username'] Time=request.GET.get('timestamp') #验证订单 result = alipay.api_alipay_trade_query(orderID) print(result) if(result['code']!='10000'): return JsonResponse({'status': 0, 'msg': '失败'}) obj_charge=charge.objects.get(uuid=orderID) amount=obj_charge.amount obj_charge.status=1 obj_charge.Time=Time obj_charge.save() obj_user=userInformation.objects.get(username=UserID) obj_user.remain=obj_user.remain+amount obj_user.save() #连接区块链 web3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545')) with open('tool/test.abi') as file: myabi = json.loads(file.read()) contract = web3.eth.contract(address=address, abi=myabi) if web3.eth.getBlock(0) is None: print("连接失败") elif web3.isConnected(): print("连接成功") #上传到区块链 contract.functions.AddMoney(UserID,int(amount*100)).transact({'from':web3.eth.accounts[0]}) return HttpResponse("支付成功请关闭该页面") 图像特征

一方面为了防止图像重复上传,一方面需要保证图像确权的唯一性,所以要在图像上传时采用提取图像特征。开始计划使用图像哈希算法:phash,ahash,dhash。发现效果并不理想 参考: https://segmentfault.com/a/1190000038308093?utm_source=sf-related http://itindex.net/detail/42723-%E7%9B%B8%E4%BC%BC-%E5%9B%BE%E7%89%87%E6%90%9C%E7%B4%A2-%E5%93%88%E5%B8%8C 于是选用图像特征算法orb,主要完成以下功能:

自动对图像进行过滤,阻止相似图像重复上传与重复版权登记。当用户上传图像。系统利用ORB算法自动提取图片特征。然后通过与保存的已上传图像的特征进行对比,防止相似图像的再次上传。即使图像经过裁剪,旋转,缩放等操作,依然能对相似图片进行有效的过滤。从而确保图像确权的有效性。 提取图像特征,作为版权信息存证。在图像进行确权时需要将图像的特征作为版权信息之一进行存证。保存的特征主要包含图像关键点和关键点对应的特征向量。这些信息将作为侵权判定的依据,对于疑似侵权图片,可以提取其特征与已经确权的图像的特征进行对比,找出相似图像,从而对侵权行为进行判定。 特征提取

得到待上传图像后,先转化为灰度图像,再通过ORB算法提取得到图像的关键点。确定一个关键点的大概过程为:

先从图片中选取一个像素点,像素值为p,并设定一个阈值t,如果两个像素相差的绝对值大于阈值,即它们的像素之差不在(p-t,p+t)这一范围,那么则判定这两个像素点不同。 将选取的点与周围的16个像素点进行对比,如果周围的点中有连续的n个点与选取的点不同,则认为改点是一个关键点。

然后确定关键点所对应的特征向量:

选取一个关键点,并以它为中心,在周围划定一个像素区域。 在划定的区域中随机选取256个像素对。对每一个像素对进行对比,如果第一个像素值大于第二个像素值,则记为1,否则记为0。最终为该关键点创建由256位0,1比特组成的特征向量 对下一个关键点重复上述操作,最终完成特征向量的提取。 特征对比

得到图像的特征信息后就可以与已保存的图像特征对比,进行相似度检测。大致过程如下:

首先需要图像版权信息中图像特征保存的地址PictureHash遍历IPFS系统上保存的图像特征,并将其还原构造为所需的KeyPoint类型。具体过程在3中图像特征保存过程叙述。 构造匹配器对象,这里使用knnMatch进行k近邻比较,最后每个匹配的关键点都会得到最相似的两个关键点。之后对这两个关键点的欧式距离进行比较,设置条件为第一个关键点的距离需要小于第二个关键点距离的0.8倍。这样就筛选出最佳匹配的关键点。 计算最佳匹配的关键点占图像关键点的比值,设定阈值为0.1,只有大于阈值才认为两幅图像相似。如果相似则返回匹配的对象DMatch,否则返回false。 当两幅图像判定相似后,则不允许上传,并且通过返回的DMatch对象对两幅图像疑似相似特征进行可视化展示,向用户说明情况。如果返回的是false,则读取下一幅图像特征,直到所有图片匹配完成且都没有相似情况,则过滤完成,图像可以上传。 特征保存

存储设计如下: 在对图像进行确权前需要通过IPFS系统将图像特征进行保存,并得到图像特征的哈希地址这一版权信息。为了方便图像特征的保存和读取设计实现了如下流程:

将已经提取的图像的关键点统一存储在列表的逻辑结构,对每一个KeyPoint类的关键点读取其属性值,转化为字典结构的点的类型。 将转化后的关键点及其特征向量分别保存在以keypoints和descriptor为键值名的两对键值对中。 通过IPFS客户端接口add_json将特征以json格式上传至IPFS系统,并得到IPFS基于内容哈希得到的链接地址PictureHash。读取特征时,通过IPFS客户端的接口get_json读取json格式的已上传图片的特征。之后将特征还原构造,过程为上述过程的逆过程。 程序及参考

https://www.cnblogs.com/alexme/p/11345701.html https://www.cnblogs.com/ronny/p/4081362.html https://blog.csdn.net/gukedream/article/details/87040212 https://www.cnblogs.com/alexme/p/11353137.html https://www.cnblogs.com/alexme/p/11345951.html https://blog.csdn.net/yang843061497/article/details/38553765

import cv2 import matplotlib.pyplot as plt import numpy as np #为了在pycharm中输出全 np.set_printoptions(threshold=np.inf) #计算图像特征 #输入读入的图像 #输出特征点和对应的描述子 def orb(img): img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #这里的参数都可以改 orb = cv2.ORB_create(500, 2.0) keypoints, descriptor = orb.detectAndCompute(img_gray, None) feature={"keypoints":keypoints,"descriptor":descriptor} return feature #这里转成了字典json的形式 #这里涉及到keypoint类,参考: #https://docs.opencv.org/2.4.9/modules/features2d/doc/common_interfaces_of_feature_detectors.html#keypoint #https://blog.csdn.net/u010821666/article/details/52883580 #https://docs.opencv.org/2.4.9/modules/features2d/doc/common_interfaces_of_feature_detectors.html#Point2f%20pt def changeFormat(feature): keypoints=feature['keypoints'] descriptor=feature['descriptor'] keypoint=[] for i in keypoints: keypoint.append({"pt":i.pt,"size":i.size,"angle":i.angle,"response":i.response,"octave":i.octave,"class_id":i.class_id}) feature={"keypoints":keypoint,"descriptor":descriptor.tolist()} return feature #读入保存的特征 def backFormat(charater): keypoint = [] for i in charater['keypoints']: keypoint.append( cv2.KeyPoint(x=i['pt'][0], y=i['pt'][1], _size=i['size'], _angle=i['angle'], _response=i['response'], _octave=i['octave'], _class_id=i['class_id'])) feature = {"keypoints": keypoint, "descriptor": np.array(charater['descriptor'], dtype='uint8')} return feature #两张图相似度的比较,不相似输出false,相似输出匹配,参考 #http://www.woshicver.com/Sixth/5_9_%E7%89%B9%E5%BE%81%E5%8C%B9%E9%85%8D/ #https://cloud.tencent.com/developer/article/1470407 #https://www.jb51.cc/python/185807.html #https://blog.csdn.net/weixin_44072651/article/details/89262277 #https://www.cnblogs.com/Lin-Yi/p/9433942.html #https://blog.csdn.net/weixin_37565420/article/details/79090644 def contrast(feature1,feature2): # bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck = True) # matches = bf.match(feature1['descriptor'], feature2['descriptor']) #与上不同,基于KNN的 bf = cv2.BFMatcher(cv2.NORM_HAMMING) matches = bf.knnMatch(feature1['descriptor'], trainDescriptors=feature2['descriptor'], k=2) # print(matches) matches= [m for (m, n) in matches if m.distance < 0.8 * n.distance] # print(matches) print(len(matches) / len(feature1['keypoints'])) if(len(matches)/max(len(feature1['keypoints']),len(feature2))>0.1): return matches else: return False #一个可视化的展示 def showMore(matches,feature1,feature2,img1,img2): img_gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) img_gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY) result = cv2.drawMatches(img_gray1, feature1['keypoints'], img_gray2, feature2['keypoints'], matches, None, flags=2) cv2.imwrite('media/similar/63.jpg',result) # plt.imshow(result) # plt.show() 图像上传

上传采用element-ui的组件el-upload,通过action上传,on-success接收后端的处理结果,on-success绑定upload函数

//该函数通过后端返回结果进行判断,如果上传图片与已上传图片有相似,展示相似图片 upload(response, file, fileList){ // location.reload() console.log(response,file, fileList); const status = response.status; if (status === 1) { this.$message({ message: response.msg, showClose: true, type: "success", }); } else if(status===2){ this.$message({ message: response.msg, showClose: true, type: "warning", }); //为了防止缓存导致不刷新,请求时通过Math.random()加入一个随机数 this.similar= this.baseURL+'media/'+response.similar+'?'+Math.random() this.DialogSimilarVisible=true }else{ this.$message({ message: response.msg, showClose: true, type: "warning", }); } },

后端的处理过程

def upload(request): #得到上传的图片 #cv2从图片文件流获取image对象:https://www.imooc.com/wenda/detail/549505 data=request.FILES.get('file') if not data: return JsonResponse({'status':0,'msg':'图片不存在'}) #得到图片 fileNPArray = np.fromstring(data.read(), np.uint8) img = cv2.imdecode(fileNPArray, cv2.IMREAD_UNCHANGED) #得到图片各属性 sessionID=request.POST.get('OwnerID') session = Session.objects.get(pk=sessionID) OwnerID=session.get_decoded()['username'] # PictureHash=phash.phash(img) feature2=orb.orb(img) name=data.name UploadTime=int(time.time()) #过滤相似图片 obj_images = image.objects.all().values('PictureHash','name') client = ipfshttpclient.connect('/dns/localhost/tcp/5001/http') images=list(obj_images) for f in images: charater = client.get_json(f['PictureHash']) feature1 = orb.backFormat(charater) matche=orb.contrast(feature2,feature1) print(matche) if(matche!=False): print(555) # img1=cv2.imread('media/'+f['name'],cv2.IMREAD_UNCHANGED) #读中文文件名 img1 = cv2.imdecode(np.fromfile('media/'+f['name']), cv2.IMREAD_UNCHANGED) orb.showMore(matche,feature2,feature1,img,img1) return JsonResponse({'status': 2, 'msg': '图片相似','similar':'similar/63.jpg'}) #特征上传至ipfs # client = ipfshttpclient.connect('/dns/localhost/tcp/5001/http') print('22') feature2 = orb.changeFormat(feature2) PictureHash= client.add_json(feature2) #连接区块链 web3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545')) with open('tool/test.abi') as file: myabi = json.loads(file.read()) contract = web3.eth.contract(address=address, abi=myabi) if web3.eth.getBlock(0) is None: print("连接失败") elif web3.isConnected(): print("连接成功") #上传到区块链 tx_hash=contract.functions.Upload(name, PictureHash, OwnerID, UploadTime).transact({'from':web3.eth.accounts[0]}) receipt = web3.eth.getTransactionReceipt(tx_hash) print(receipt) print(contract.events.backPictureID().processReceipt(receipt)) PictureID=contract.events.backPictureID().processReceipt(receipt)[0]['args'][''] #上传到数据库 UploadTime=time.asctime(time.localtime(UploadTime)) print(UploadTime) obj_image=image(PictureID=PictureID,name=str(PictureID)+'_'+name,PictureHash=PictureHash,OwnerID=OwnerID,UpLoadTime=UploadTime) obj_image.save() obj_user=userInformation.objects.get(username=OwnerID) obj_user.UpNumber= obj_user.UpNumber+1 obj_user.save() #保存图片 fpath=os.path.join(settings.MEDIA_ROOT,str(PictureID)+'_'+name) print(fpath) with open(fpath,'wb') as f: for i in data.chunks(): f.write(i) #添加可见水印 createwatermark.new_logo(str(PictureID)+'_'+name) return JsonResponse({'status': 1, 'msg': '上传成功'})

如图所示,即使图片被裁减,也能判定相似,阻止上传

图像下载

当图像下载时,为了防止侵权行为的发生,需要将下载记录保存在区块链中。同时为了更好保护图片所有者的利益,下载者需要支付所有者为图片设置的金额后才能下载使用图片。具体过程设计如下:

前端生成uuid,产生订单信息,后端调用智能合约查询账户余额与图片价格进行对比,余额不足则禁止下载,反之进入下一步。 得到用户编号UserID,下载图片编号PictureID,下载时间DownTime,通过智能合约保存在区块链中,保存为两层映射,通过下载编号映射到下载者,下载者为一个结构体,包含下载者编号和下载时间。再通过PictureID映射到该映射,具体设计见4.3节智能合约的设计部分。最后将相关信息同步至数据库保存。 根据下载用户信息和图片信息生成水印,并嵌入到图像中,再返回给用户

前端

download(PictureID){ var that = this; that.axios .post(that.baseURL + "download/",{ sessionID: sessionStorage.sessionID, PictureID:PictureID, uuid:guid() }) .then((response) => { const status = response.data.status; if (status === 1) { that.$message({ message: response.data.msg, showClose: true, type: "success", }); //图片的下载 let link = document.createElement('a') let url = that.baseURL+'media/'+response.data.url //codeIMG 要下载的路径 fetch(url).then(res => res.blob()).then(blob=>{ link.href = URL.createObjectURL(blob) console.log(link.href) link.download = 'img' document.body.appendChild(link) link.click() }) } else { that.$message({ message: response.data.msg, showClose: true, type: "warning", }); } }) .catch(function (err) { that.$message({ message: err, showClose: true, type: "warning", }); }); },

后端

def download(request): #连接区块链 web3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545')) with open('tool/test.abi') as file: myabi = json.loads(file.read()) contract = web3.eth.contract(address=address, abi=myabi) if web3.eth.getBlock(0) is None: print("连接失败") elif web3.isConnected(): print("连接成功") #得到信息 data=json.loads(request.body.decode('utf-8')) PictureID=data['PictureID'] sessionID=data['sessionID'] session = Session.objects.get(pk=sessionID) UserID=session.get_decoded()['username'] uuid=data['uuid'] DownTime = int(time.time()) obj_img = image.objects.get(PictureID=PictureID) amount=obj_img.interest #验证余额 remains=contract.functions.GetMoney(UserID).call() if(remains/100= 0 and j + 1 < size): j = j + 1 i = i - 1 dct_list.append(dct_array[i][j]) count = count + 1 # 扫到头右移 if (j + 1 < size): j = j + 1 dct_list.append(dct_array[i][j]) count = count + 1 else: i = i + 1 dct_list.append(dct_array[i][j]) count = count + 1 # 向下扫 while (j - 1 >= 0 and i + 1 < size): i = i + 1 j = j - 1 dct_list.append(dct_array[i][j]) count = count + 1 # 扫到头下移 if (i + 1 < size): i = i + 1 dct_list.append(dct_array[i][j]) count = count + 1 else: j = j + 1 dct_list.append(dct_array[i][j]) count = count + 1 return dct_list element-ui布局参考

https://www.freesion.com/article/110081469/

使用vue-cli编译时报错 Couldn't parse bundle asset

更新node版本,可能是vue版本冲突

django配置mysql报错

解决方案:https://www.cnblogs.com/jiaoyang77/p/9333424.html

django跨域解决

https://www.cnblogs.com/lowmanisbusy/p/9589432.html

生成11位随机编号的代码

rand = "{0:0>11}".format(random.randint(0, 99999999999))



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有